package ginv

import (
	"context"
	"gitee.com/filters/logger"
	"gitee.com/filters/utils/character"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/google/uuid"
	"github.com/spf13/cast"
	"net/http"
	"reflect"
	"time"
)

var (
	typeContext    = reflect.TypeOf(new(context.Context)).Elem()
	typeError      = reflect.TypeOf(new(error)).Elem()
	defaultContext = &gin.Context{}
	DefaultGinTime = 10 * time.Second
	ctx            = context.Background()
)

func GetGin(ctx context.Context) *gin.Context {
	return defaultContext
}

func GetCtx(ctxs context.Context) context.Context {
	ctx, cancel := context.WithTimeout(ctx, DefaultGinTime)
	defer cancel()
	return ctx
}

func GinSet(ctx context.Context, key string, value any) {
	defaultContext.Set(key, value)
}

func GinGet(ctx context.Context, key string) (value any, exists bool) {
	value, exists = defaultContext.Get(key)
	return
}

//WrapHandler ...
func WrapHandler(h interface{}) gin.HandlerFunc {
	if h == nil {
		panic("WrapHandler不能传入空")
	}
	t := reflect.TypeOf(h)
	if t.NumIn() < 3 {
		panic("入参必须填三个参数")
	}
	if reflect.TypeOf(h).Kind() != reflect.Func {
		panic("入参必须为函数")
	}
	if typeContext.AssignableTo(t.In(0)) && t.Out(0).AssignableTo(typeError) {
		return wrapType31(reflect.ValueOf(h))
	}
	panic("未找到此函数")
}

func WrapStringHandler(h interface{}) gin.HandlerFunc {

	if h == nil {
		panic("WrapHandler不能传入空")
	}
	t := reflect.TypeOf(h)
	if t.NumIn() < 3 {
		panic("入参必须填三个参数")
	}
	if reflect.TypeOf(h).Kind() != reflect.Func {
		panic("入参必须为函数")
	}
	if typeContext.AssignableTo(t.In(0)) && t.Out(0).AssignableTo(typeError) {
		return wrapStringType31(reflect.ValueOf(h))
	}
	panic("未找到此函数")
}


func wrapStringType31(hv reflect.Value) gin.HandlerFunc {
	t := hv.Type()
	return func(c *gin.Context) {
		var reqValue []reflect.Value
		var rspV reflect.Value
		defaultContext = c
		ctx = context.WithValue(c, logger.RequestId, character.GetUid())
		ctx, cancel := context.WithTimeout(ctx, DefaultGinTime)
		defer cancel()
		reqValue = append(reqValue, reflect.ValueOf(ctx))
		for i := 1; i < t.NumIn(); i++ {
			reqV, _ := inparam(c, t.In(i).Elem())
			reqValue = append(reqValue, reqV)
			if i == 2 {
				rspV = reqV
			}
		}
		out := hv.Call(reqValue)
		es, _ := out[0].Interface().(error)
		DefaultStringRspRenderer(c, rspV.Interface(), es)
	}
}

func wrapType31(hv reflect.Value) gin.HandlerFunc {
	t := hv.Type()
	return func(c *gin.Context) {
		var reqValue []reflect.Value
		var rspV reflect.Value
		defaultContext = c
		ctx = context.WithValue(c, logger.RequestId, uuid.New().String())
		ctx, cancel := context.WithTimeout(ctx, DefaultGinTime)
		defer cancel()
		//GinSet(c, RequestId, uuid.New().String())
		reqValue = append(reqValue, reflect.ValueOf(ctx))
		for i := 1; i < t.NumIn(); i++ {
			reqV, _ := inparam(c, t.In(i).Elem())
			reqValue = append(reqValue, reqV)
			if i == 2 {
				rspV = reqV
			}
		}
		out := hv.Call(reqValue)
		es, _ := out[0].Interface().(error)
		DefaultRspRenderer(c, rspV.Interface(), es)
	}
}

func inparam(c *gin.Context, reqs reflect.Type) (reflect.Value, error) {
	var bs []binding.Binding
	b := binding.Default(c.Request.Method, c.ContentType())
	if b == binding.Form {
		bs = []binding.Binding{b, binding.Header}
	} else {
		bs = []binding.Binding{b, binding.Header, binding.Form}
	}
	reqV := reflect.New(reqs)
	req := reqV.Interface()
	for _, b := range bs {
		err := c.ShouldBindWith(req, b)
		if err != nil {
			return reqV, err
		}
	}
	err := c.ShouldBindUri(req)
	if err != nil {
		return reflect.Value{}, err
	}
	return reqV, nil
}

// DefaultRspRenderer 默认的请求结果处理函数
func DefaultRspRenderer(c *gin.Context, rsp interface{}, err error) {
	if err != nil {
		_ = c.Error(err)
		return
	}
	c.JSON(http.StatusOK, rsp)
}

// DefaultStringRspRenderer 默认的String请求结果处理函数
func DefaultStringRspRenderer(c *gin.Context, rsp interface{}, err error) {
	if err != nil {
		_ = c.Error(err)
		return
	}
	rt := reflect.TypeOf(rsp)
	vt := reflect.ValueOf(rsp)
	var str = ""
	for i := 0; i < rt.Elem().NumField(); i++ {
		if rt.Elem().Field(i).Type.Kind() != reflect.String {
			c.String(http.StatusOK, "格式不对")
			return
		}
		str = str + cast.ToString(vt.Elem().Field(i))
	}
	c.String(http.StatusOK, str)
}
